// SPDX-License-Identifier: GPL-2.0
/*
 * soft_reset.c - Lombo soft reset driver module
 *
 * Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <linux/device.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/input.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>

#include <mach/debug.h>

struct soft_reset {
	u32 irq;
	u32 gpio_num;
	struct input_dev *idev;
};

static irqreturn_t soft_reset_irq_handler(int irq, void *data)
{
	struct soft_reset *sr = data;
	int value;

	value = __gpio_get_value(sr->gpio_num);
	PRT_ERR("gpio %d input is %d\n", sr->gpio_num, value);
	if (value == 0)
		input_report_key(sr->idev, KEY_9, 1);
	else if (value == 1)
		input_report_key(sr->idev, KEY_9, 0);

	input_sync(sr->idev);

	return IRQ_HANDLED;
}

static int soft_reset_probe(struct platform_device *pdev)
{
	int ret, gpio, irq;
	struct soft_reset *sr;
	struct device_node *np = pdev->dev.of_node;

	sr = kzalloc(sizeof(struct soft_reset), GFP_KERNEL);
	if (sr == NULL)
		return -EINVAL;

	gpio = of_get_named_gpio(np, "gpios", 0);
	if (gpio < 0) {
		dev_err(&pdev->dev, "gpios property not found\n");
		ret = -EINVAL;
		goto err_irq;
	}

	if (!gpio_is_valid(gpio)) {
		dev_err(&pdev->dev, "gpio %d invalid\n", gpio);
		ret = -EINVAL;
		goto err_irq;
	}
	sr->gpio_num = gpio;

	ret = devm_gpio_request(&pdev->dev, gpio, "input");
	if (ret < 0) {
		dev_err(&pdev->dev, "request %d fail\n", gpio);
		goto err_irq;
	}

	ret = gpio_direction_input(gpio);
	if (ret < 0) {
		dev_err(&pdev->dev, "set %d input fail\n", gpio);
		goto err_irq;
	}

	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		dev_err(&pdev->dev, "get irq %d fail\n", irq);
		ret = -EINVAL;
		goto err_irq;
	}

	ret = request_irq(irq, soft_reset_irq_handler, IRQ_TYPE_EDGE_BOTH,
					"soft-reset-irq", (void *)sr);
	if (unlikely(ret)) {
		dev_err(&pdev->dev, "request irq %d failed\n", irq);
		goto err_irq;
	}
	sr->irq = irq;

	sr->idev = input_allocate_device();
	if (sr->idev == NULL) {
		pr_err("%s:%d, allocate input device failed\n",
			__func__,
			__LINE__);
		goto err_alloc;
	}

	set_bit(EV_KEY, sr->idev->evbit);
	set_bit(KEY_9, sr->idev->keybit);

	ret = input_register_device(sr->idev);
	if (ret) {
		pr_err("%s:%d, register input device failed\n",
			__func__,
			__LINE__);
		goto err_inp;
	}

	platform_set_drvdata(pdev, (void *)sr);
	PRT_INFO("soft_reset probe success\n");
	goto out;

err_inp:
	input_free_device(sr->idev);
err_alloc:
	free_irq(irq, (void *)sr);
err_irq:
	kfree(sr);
out:
	return ret;
}


static int soft_reset_remove(struct platform_device *pdev)
{
	struct soft_reset *sr = platform_get_drvdata(pdev);

	input_free_device(sr->idev);
	free_irq(sr->irq, (void *)sr);
	kfree(sr);

	return 0;
}

#ifdef CONFIG_OF
static const struct of_device_id soft_reset_dt_match[] = {
	{ .compatible = "soft-reset" },
	{}
};
MODULE_DEVICE_TABLE(of, soft_reset_dt_match);
#endif

static struct platform_driver soft_reset_driver = {
	.probe		= soft_reset_probe,
	.remove		= soft_reset_remove,
	.driver		= {
		.name	= "lombo-soft-reset",
		.owner	= THIS_MODULE,
	#ifdef CONFIG_OF
		.of_match_table = of_match_ptr(soft_reset_dt_match),
	#endif
	},
};

static int __init soft_reset_driver_init(void)
{
	return platform_driver_register(&soft_reset_driver);
}
module_init(soft_reset_driver_init);

static void __exit soft_reset_driver_exit(void)
{
	platform_driver_unregister(&soft_reset_driver);
}
module_exit(soft_reset_driver_exit);

MODULE_DESCRIPTION("LomboTech gpadc driver");
MODULE_AUTHOR("lomboswer@lombotech.com");
MODULE_LICENSE("GPL");
